/**
 * \file Symbol.h
 * \brief Headers for SymbolTable and Symbol subclasses
 * \author Wouter Caarls <w.caarls@tnw.tudelft.nl> 
 *
 * \verbatim
 * Copyright (c) 2005 Wouter Caarls, Delft University of Technology
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * \endverbatim
 */

#ifndef __SYMBOL_H_INCLUDED
#define __SYMBOL_H_INCLUDED

#include <iostream>
#include <list>
#include "Type.h"
#include "Base.h"
#include "Declaration.h"
#include "Value.h"

class TSymbol
{
  public:
    TSymbol() : identifier(""), index(0), set(0), owner(NULL) {}
    TSymbol(std::string _identifier) : identifier(_identifier), index(0), set(0), owner(NULL) {}
    TSymbol(std::string _identifier, bool _set) :
      identifier(_identifier), index(0), set(_set), owner(NULL) {}
 
    virtual ~TSymbol() {}

    virtual TSymbol* copy() = 0;

    virtual Code* reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed) = 0;
    virtual std::string toString(int indentation=0) = 0;
  public:
    std::string identifier;
    int index;
    bool set;
    SymbolTable *owner;
};

class Variable : public TSymbol
{
  public:
    Variable() : TSymbol(), data(NULL), decl(NULL) {}
    Variable(std::string _identifier) : TSymbol(_identifier), data(NULL), decl(NULL) {}
    Variable(std::string _identifier, Value *_data) :
      TSymbol(_identifier), data(_data), decl(NULL) {}
    Variable(std::string _identifier, Value *_data, Declaration2 *_decl) :
      TSymbol(_identifier), data(_data), decl(_decl) {}
    Variable(std::string _identifier, Value *_data, Declaration2 *_decl, bool _set) :
      TSymbol(_identifier, _set), data(_data), decl(_decl) {}

    ~Variable()
    {
      delete data;
    }

    Variable* copy()
    {
      if (data)	
	return new Variable(identifier, data->copy(), decl, set);
      else
	return new Variable(identifier);
    }

    Expression* reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);
    std::string toString(int indentation=0);

  public:
    Value *data;
    Declaration2 *decl;
};

class Function : public TSymbol
{
  public:
    Function() : TSymbol(), data(NULL), def(NULL), decl(NULL), isExtern(false) {}
    Function(std::string _identifier, FunDef *_data=NULL, FunDef *_def=NULL,
	     Declaration2 *_decl=NULL, bool _isExtern=false) :
      TSymbol(_identifier), data(_data), def(_def), decl(_decl), isExtern(_isExtern) {}

    ~Function()
    {
      delete data;
    }

    virtual Function* copy()
    {
      if (data)
	return new Function(identifier, data->copy(), def, decl, isExtern);
      else
	return new Function(identifier);
    }

    virtual Code* reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);
    virtual std::string toString(int indentation=0);

  public:
    FunDef *data;
    FunDef *def;
    Declaration2 *decl;
    bool isExtern;
};

class Strategy : public Function
{
  public:
    Strategy() : Function() {}
    Strategy(std::string _identifier, FunDef *_data=NULL, FunDef *_def=NULL) :
      Function(_identifier, _data, _def) {}

    Strategy* copy()
    {
      if (data)
	return new Strategy(identifier, data->copy(), def);
      else
	return new Strategy(identifier);
    }

    Code* reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);
    std::string toString(int indentation=0);
};

class TypeSymbol : public TSymbol
{
  public:
    TypeSymbol() : TSymbol(), data(NULL) {}
    TypeSymbol(std::string _identifier) : TSymbol(_identifier), data(NULL) {}
    TypeSymbol(std::string _identifier, Type *_data, TypeDef *_def=NULL) :
      TSymbol(_identifier), data(_data), def(_def)
    {
      data->sym = this;
    }

    ~TypeSymbol()
    {
      // *** SHOULD ONLY DELETE FOR SYSTEM-CONSTRUCTED TYPEDEFS ***
      //delete data;
    }

    TypeSymbol* copy()
    {
      if (data)
	return new TypeSymbol(identifier, data->copy(), def);
      else
	return new TypeSymbol(identifier);
    }

    Code* reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);
    std::string toString(int indentation=0);

  public:
    Type *data;
    TypeDef *def;
};

class Scope : public TSymbol
{
  public:
    Scope() : TSymbol() {}
    Scope(std::string _identifier) : TSymbol(_identifier) {}

    Scope* copy()
    {
      return new Scope(identifier);
    }

    Expression* reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed)
    {
      return NULL;
    }
    
    std::string toString(int indentation=0)
    {
      return (char*)(CStr(indent(indentation)) << "--- FRAME ---");
    }
};

class Id;

/*
 * IMPORTANT NOTICE:
 * The symbol table implementation has a giant memory leak for type symbols:
 * As (through promotion and the reduce builtin) types can transcend stack
 * frames and spawned symbol tables, they are *NOT* deleted.
 * This should be handled by reference counting!
 */
class SymbolTable
{
  public:
    typedef std::list<TSymbol*>::iterator TI;
    typedef std::list<TSymbol*>::reverse_iterator RTI;

    SymbolTable() : start(0)
    {
      LOG(plog.crawl, HDR << "Created new symbol table " << this << std::endl);
    
      newFrame();
    }

    ~SymbolTable()
    {
      LOG(plog.crawl, HDR << "Destroying symbol table " << this << std::endl);

      for (TI ii=table.begin(); ii != table.end(); ++ii)
        if ((*ii)->owner == this)
          if (typeid(**ii) != typeid(TypeSymbol))
  	    delete (*ii);
    }

    SymbolTable *copy()
    {
      SymbolTable *res = new SymbolTable();
      
      // Get rid of automatic first frame
      res->popFrame();
      res->start = start;
      
      for (TI ii=table.begin(); ii != table.end(); ++ii)
	res->addSymbol((*ii)->copy());

      return res;
    }

    SymbolTable *spawn()
    {
#if 1
//      LOG(plog.crawl, HDR << "Spawning symbol table of " << table.size() << " elements" << std::endl);

      SymbolTable *res = new SymbolTable();
      
      // Get rid of automatic first frame
      res->popFrame();
      res->start = start;
      
      for (TI ii=table.begin(); ii != table.end(); ++ii)
	res->addSymbol(*ii, false, true);

      return res;
#else
      return copy();
#endif
    }

    void newFrame();
    void newFrame(std::string identifier);
    void popFrame();

    Scope *getScope(std::string identifier);

    void addSymbol(TSymbol *symbol, bool addToFront=false, bool copyOnWrite=false);
    TSymbol *findSymbol(std::string identifier);
    TSymbol *getSymbol(int index);

    void addReturnVariable(Type *type);
    Id *findReturnVariable();
    void popReturnVariable();
    void clear();

    void dirtyChanged(SymbolTable *rhs, bool mergeAssignments=true);

    void removeGenerators(void);
    void residualizeGenerators(void);
    
    std::string symbolToString(std::string identifier);

    std::string print();
    std::string printKnownValues();
  public:
    int start;
    std::list<TSymbol*> table;
};

#endif /* __SYMBOL_H_INCLUDED */

